/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.lcm.metadata.core;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.inspur.edp.lcm.metadata.api.entity.ExternalInfo;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.GspProject;
import com.inspur.edp.lcm.metadata.api.entity.MetadataPackageHeader;
import com.inspur.edp.lcm.metadata.api.entity.MetadataProject;
import com.inspur.edp.lcm.metadata.api.entity.Module;
import com.inspur.edp.lcm.metadata.api.entity.OperationEnum;
import com.inspur.edp.lcm.metadata.api.entity.ProcessMode;
import com.inspur.edp.lcm.metadata.api.entity.ProjectHeader;
import com.inspur.edp.lcm.metadata.api.entity.bo.BOInfo;
import com.inspur.edp.lcm.metadata.api.entity.uri.MetadataURI;
import com.inspur.edp.lcm.metadata.api.mvnEntity.MavenPackageRefs;
import com.inspur.edp.lcm.metadata.api.service.RefCommonService;
import com.inspur.edp.lcm.metadata.common.FileServiceImp;
import com.inspur.edp.lcm.metadata.common.Graph;
import com.inspur.edp.lcm.metadata.common.MetadataSerializer;
import com.inspur.edp.lcm.metadata.common.Utils;
import com.inspur.edp.lcm.metadata.common.Vertex;
import com.inspur.edp.lcm.metadata.core.entity.MdprojInfoDto;
import com.inspur.edp.lcm.metadata.core.manager.PomManager;
import com.inspur.edp.lcm.metadata.core.persistence.RepositoryFactory;
import com.inspur.edp.lcm.metadata.devcommon.ManagerUtils;
import com.inspur.edp.lcm.metadata.inner.api.BoService;
import com.inspur.edp.lcm.metadata.inner.api.utils.BoUtils;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

@Slf4j
public class MetadataProjectCoreService {
    private static MetadataProjectCoreService current;

    public static MetadataProjectCoreService getCurrent() {
        if (current == null) {
            current = new MetadataProjectCoreService();
        }
        return current;
    }

    private final FileServiceImp fileService = new FileServiceImp();

    public void create(String projectPath, String projectName, String projectNameSpace, String packageName, String processMode) {
        try {
            MetadataProject metadataProject = RepositoryFactory.getInstance().getMetadataProjectRepository().assmbllyMetadataProj(projectName, projectNameSpace, packageName);

            // 解析型
            metadataProject.getMetadataPackageInfo().setProcessMode(ProcessMode.valueOf(processMode));

            String metadataProjStr = buildMetadataProjectStr(metadataProject);

            RepositoryFactory.getInstance().getMetadataProjectRepository().add(projectPath, metadataProjStr);
        } catch (Exception e) {
            throw new RuntimeException("元数据工程创建报错", e);
        }
    }

    public void createGspProject(String absolutePath, GspProject projectData) {
        // 将GSPProject实体转为文件流
        String projectStr = buildGspProjectStr(projectData);
        //全路径
        String projectPath = Paths.get(absolutePath).resolve(Utils.getGspProjectName()).toString();
        RepositoryFactory.getInstance().getMetadataProjectRepository().add(projectPath, projectStr);
    }

    private String buildMetadataProjectStr(MetadataProject metadataProject) {
        try {
            ObjectMapper mapper = Utils.getMapper();
            return mapper.writeValueAsString(metadataProject);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    private String buildGspProjectStr(GspProject gspProject) {
        try {
            ObjectMapper mapper = Utils.getMapper();
            return mapper.writeValueAsString(gspProject);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public void rename(String absolutePath, String oldName, String newName) {
        try {
            Utils.checkEmpty(oldName, "oldName");
            Utils.checkEmpty(newName, "newName");
            Utils.checkEmpty(absolutePath, "absolutePath");
            String oldFilePath = Paths.get(absolutePath).resolve(oldName).toString();
            //要重命名的文件不存在
            if (!fileService.isFileExist(oldFilePath)) {
                throw new RuntimeException("文件不存在" + oldFilePath);
            } else if (!Utils.nameValidating(newName)) {
                throw new RuntimeException("文件命名不规范" + newName);
            } else if (oldName.equals(newName)) {
                return;
            }

            String newFilePath = Paths.get(absolutePath).resolve(newName).toString();
            //已经存在新名称的文件
            if (fileService.isFileExist(newFilePath)) {
                throw new RuntimeException("文件已经存在" + newFilePath);
            }
            //重命名
            fileService.fileRename(oldFilePath, newFilePath);
            //修改文件内容
            //RepositoryFactory.getInstance().getMetadataProjectRepository().updateBaseInfo(newFilePath, newName);
        } catch (Exception e) {
            throw new RuntimeException("工程重命名失败", e);
        }
    }


    public MetadataProject getMetadataProjInfo(String absolutePath) {
        String projPath = getProjPath(absolutePath);
        if (StringUtils.isEmpty(projPath)) {
            return null;
        }
        absolutePath = fileService.getCombinePath(projPath, Utils.getMetadataProjPath());
        if (isExistProjFile(absolutePath)) {
            return getMetadataProjDirectly(absolutePath);
        } else {
            List<String> projPathList = new LinkedList<>();
            return getMetadataProjRecursively(absolutePath, projPathList);
        }
    }

    public ProcessMode getProcessMode(String absolutePath) {
        final MetadataProject metadataProjInfo = getMetadataProjInfo(absolutePath);
        Utils.checkNPE(metadataProjInfo, "无法获取元数据工程信息：" + absolutePath);
        return metadataProjInfo.getMetadataPackageInfo().getProcessMode() == null ? ProcessMode.generation : metadataProjInfo.getMetadataPackageInfo().getProcessMode();
    }

    public boolean metadataChangesDetected(String absolutePath) {
        Map<String, Long> metadataModifiedTime = getMetadataModifiedTime(absolutePath);
        Map<String, Map<String, Long>> sourceDataModifiedTime = getSourceDataModifiedTime(absolutePath);
        if (sourceDataModifiedTime.get("metadata").size() == metadataModifiedTime.size()) {
            for (String key : metadataModifiedTime.keySet()) {
                if (!metadataModifiedTime.get(key).equals(sourceDataModifiedTime.get("metadata").get(key))) {
                    return true;
                }
            }
        } else {
            return true;
        }
        return false;
    }

    public boolean isExistProjFile(String absolutePath) {
        List<File> files = fileService.getAllFiles(absolutePath);
        if (files == null || files.size() == 0) {
            return false;
        }
        for (File file : files) {
            if (file.getName().equals(Utils.getGspProjectName())) {
                return true;
            }
        }

        return false;
    }

    public MetadataProject getMetadataProjRecursively(String absolutePath, List<String> projectPath) {
        if (absolutePath.isEmpty()) {
            return null;
        }

        List<File> files = fileService.getAllFiles(absolutePath);
        // 目录下没文件，继续往上级目录下找
        if (files == null || files.size() == 0) {
            String parentPath = absolutePath.substring(0, absolutePath.lastIndexOf('/'));
            return getMetadataProjRecursively(parentPath, projectPath);
        } else {
            // 遍历目录下的文件
            for (File item : files) {
                if (fileService.getExtension(Utils.handlePath(item.getPath())).equals(Utils.getMetadataProjSuffix())) {
                    if (!projectPath.contains(Utils.handlePath(item.getPath()))) {
                        projectPath.add(Utils.handlePath(item.getPath()));
                    }
                }
            }
            if (projectPath.size() == 1) { // 如果找到工程文件且仅有一个则返回不再查找
                String projStr = fileService.fileRead(projectPath.get(0));

                String projPath = projectPath.get(0).substring(0, projectPath.get(0).lastIndexOf('/'));
                String projFileName = projectPath.get(0).substring(projectPath.get(0).lastIndexOf('/') + 1);
                ObjectMapper objectMapper = Utils.getMapper();
                MetadataProject project;
                try {
                    project = objectMapper.readValue(projStr, MetadataProject.class);
                    project.setProjectPath(projPath);
                    project.setProjectFileName(projFileName);
                    project.setCsprojAssemblyName(project.getMetadataPackageInfo().getName());
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
                return project;
            } else if (projectPath.size() > 1) { // 如果同一目录下有两个工程文件，则不知道更新哪个，抛异常
                try {
                    throw new IOException("同一目录下有两个工程文件");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            } else { // 没找到，则继续往上层查找
                String parentPath = absolutePath.substring(0, absolutePath.lastIndexOf('/'));
                if (parentPath.isEmpty()) {
                    return null;
                }
                return getMetadataProjRecursively(parentPath, projectPath);
            }
        }
    }

    public Map<String, Long> getMetadataModifiedTime(String absolutePath) {
        Map<String, Long> metadataModifiedTime = new HashMap<>();
        List<String> metadataPaths = new ArrayList<>();
        //获取目录下的所有文件路径
        RepositoryFactory.getInstance().getMetadataRepository().getMetadatasUnderDir(metadataPaths, absolutePath);
        for (String metadataPath : metadataPaths) {
            String extension = fileService.getExtension(metadataPath).toLowerCase();
            String result = Utils.getMetadataPostfixTypes().stream().filter(item -> item.toLowerCase().equals(extension)).findAny().orElse(null);
            if (result != null) {
                String metadataAbsoulutePath = Utils.handlePath(metadataPath);
                metadataModifiedTime.put(metadataAbsoulutePath.replace(absolutePath, "."), new File(metadataAbsoulutePath).lastModified());
            }
        }
        return metadataModifiedTime;
    }

    public boolean codeChangesDetected(String absolutePath, String type) {
        Map<String, Long> codeModifiedTime = new HashMap<>();
        if ("api_src".equals(type)) {
            Map<String, Long> apiSrcModifiedTime = getCodeModifiedTime(absolutePath, "api_src");
            return apiSrcModifiedTime == null || apiSrcModifiedTime.size() == 0 || apiSrcModifiedTime.containsKey("nofile");
        }

        if ("api".equals(type)) {
            Map<String, Long> apiSrcModifiedTime = getCodeModifiedTime(absolutePath, "api_src");
            Map<String, Long> apiTargetModifiedTime = getCodeModifiedTime(absolutePath, "api_target");
            codeModifiedTime.putAll(apiSrcModifiedTime);
            codeModifiedTime.putAll(apiTargetModifiedTime);
        } else {
            codeModifiedTime = getCodeModifiedTime(absolutePath, type);
        }
        Map<String, Map<String, Long>> sourceDataModifiedTime = getSourceDataModifiedTime(absolutePath);
        if (sourceDataModifiedTime.get(type) != null && sourceDataModifiedTime.get(type).size() == codeModifiedTime.size()) {
            for (String key : codeModifiedTime.keySet()) {
                if (!codeModifiedTime.get(key).equals(sourceDataModifiedTime.get(type).get(key))) {
                    return true;
                }
            }
        } else {
            return true;
        }
        return false;
    }

    public Map<String, Map<String, Long>> getSourceDataModifiedTime(String absolutePath) {
        return RepositoryFactory.getInstance().getMetadataProjectRepository().getSourceData(absolutePath).get("modifiedtime");
    }

    public void setSourceDataModifiedTime(String projPath, List<String> types) {
        try {
            Map<String, Map<String, Map<String, Long>>> sourceDataModifiedTime = getSourceDataModifiedTime(projPath, types);
            RepositoryFactory.getInstance().getMetadataProjectRepository().setSourceData(projPath, sourceDataModifiedTime);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void getRefProjPaths(MetadataProject metadataProjInfo, List<String> refProjPaths) {
        if (Objects.isNull(metadataProjInfo)) {
            return;
        }
        List<ProjectHeader> mdProjectRefs = metadataProjInfo.getProjectRefs();
        if (!CollectionUtils.isEmpty(mdProjectRefs)) {
            String absolutePath = getProjPath(metadataProjInfo.getProjectPath());
            for (ProjectHeader projectHeader : mdProjectRefs) {
                String refProjPath = fileService.getCombinePath(absolutePath, projectHeader.getProjectPath());
                if (fileService.isDirectoryExist(refProjPath) && !refProjPaths.contains(refProjPath)) {
                    refProjPaths.add(refProjPath);

                    // 递归查找
                    String depAbsolutePath = getProjPath(refProjPath);
                    MetadataProject depMetadataProjInfo = getMetadataProjInfo(depAbsolutePath);
                    getRefProjPaths(depMetadataProjInfo, refProjPaths);
                }
            }
        }
    }

    public void getRefProjPaths(String absolutePath, Set<String> refProjPaths) {
        absolutePath = getProjPath(absolutePath);
        MetadataProject metadataProjInfo = getMetadataProjInfo(absolutePath);
        if (metadataProjInfo != null && metadataProjInfo.getProjectRefs() != null && metadataProjInfo.getProjectRefs().size() > 0) {
            for (ProjectHeader projectHeader : metadataProjInfo.getProjectRefs()) {
                String refProjPath = fileService.getCombinePath(absolutePath, projectHeader.getProjectPath());
                if (fileService.isDirectoryExist(refProjPath) && !refProjPaths.contains(refProjPath)) {
                    refProjPaths.add(refProjPath);
                    getRefProjPaths(refProjPath, refProjPaths);
                }
            }
        }
    }

    public List<String> getBuildOrder(Map<String, MdprojInfoDto> mdprojInfoDtos) {
        List<String> projectsBuildOrder = new ArrayList<>();

        Graph<String> graph = new Graph<>(true);
        for (String mdpkgName : mdprojInfoDtos.keySet()) {
            graph.addVertex(mdpkgName);
            getDirectedGraph(mdpkgName, graph, mdprojInfoDtos);
        }

        Stack<Vertex<String>> buildOrder = graph.getTopoSort();
        for (Vertex<String> vertex : buildOrder) {
            projectsBuildOrder.add(mdprojInfoDtos.get(vertex.getLabel()).getProjPath());
        }
        return projectsBuildOrder;
    }

    private void getDirectedGraph(String mdpkgName, Graph<String> graph, Map<String, MdprojInfoDto> mdprojInfoDtos) {
        MetadataProject metadataProjInfo = mdprojInfoDtos.get(mdpkgName).getMetadataProject();
        if (metadataProjInfo.getMetadataPackageRefs() != null && metadataProjInfo.getMetadataPackageRefs().size() > 0) {
            for (MetadataPackageHeader metadataPackageHeader : metadataProjInfo.getMetadataPackageRefs()) {
                if (mdprojInfoDtos.containsKey(metadataPackageHeader.getName()) && graph.addVertex(metadataPackageHeader.getName())) {
                    graph.addEdge(mdpkgName, metadataPackageHeader.getName());
                    getDirectedGraph(metadataPackageHeader.getName(), graph, mdprojInfoDtos);
                }
            }
        }
    }

    public List<String> getBuildOrder(String absolutePath) {
        List<String> projectsBuildOrder = new ArrayList<>();

        MetadataProject metadataProjInfo = getMetadataProjInfo(absolutePath);
        if (metadataProjInfo == null || metadataProjInfo.getProjectRefs() == null || metadataProjInfo.getProjectRefs().size() == 0 || metadataProjInfo.getMavenPackageRefs() == null || metadataProjInfo.getMavenPackageRefs().size() == 0) {
            projectsBuildOrder.add(absolutePath);
            return projectsBuildOrder;
        }

        Graph<String> graph = new Graph<>(true);
        graph.addVertex(absolutePath);
        getDirectedGraph(absolutePath, graph);
        Stack<Vertex<String>> buildOrder = graph.getTopoSort();
        for (Vertex<String> vertex : buildOrder) {
            projectsBuildOrder.add(vertex.getLabel());
        }
        return projectsBuildOrder;
    }

    private void getDirectedGraph(String projPath, Graph<String> graph) {
        MetadataProject metadataProjInfo = getMetadataProjInfo(projPath);
        if (metadataProjInfo != null && metadataProjInfo.getProjectRefs() != null && metadataProjInfo.getProjectRefs().size() > 0 && metadataProjInfo.getMavenPackageRefs() != null && metadataProjInfo.getMavenPackageRefs().size() > 0) {
            for (ProjectHeader projectHeader : metadataProjInfo.getProjectRefs()) {
                String depProjPath = Utils.handlePath(fileService.getCombinePath(projPath, projectHeader.getProjectPath()));
                if (fileService.isDirectoryExist(depProjPath)) {
                    graph.addVertex(depProjPath);
                    graph.addEdge(projPath, depProjPath);
                    getDirectedGraph(depProjPath, graph);
                }
            }
        }
    }

    public void updateMavenRefVersion(String projPath, MetadataProject metadataProj, MavenPackageRefs mavenPackageRefs) {
        projPath = getProjPath(projPath);
        if (metadataProj.getMavenPackageRefs() == null) {
            metadataProj.setMavenPackageRefs(new ArrayList<>());
        }
        boolean findIt = false;
        if (metadataProj.getMavenPackageRefs().size() > 0) {
            for (MavenPackageRefs mavenPackageRef : metadataProj.getMavenPackageRefs()) {
                if (mavenPackageRef.getArtifactId().equals(mavenPackageRefs.getArtifactId())) {
                    if (mavenPackageRef.getVersion().equals(mavenPackageRefs.getVersion())) {
                        return;
                    }
                    findIt = true;
                    mavenPackageRef.setVersion(mavenPackageRefs.getVersion());
                    break;
                }
            }
        }
        if (!findIt) {
            metadataProj.getMavenPackageRefs().add(mavenPackageRefs);
        }
        Utils.writeValue(projPath, metadataProj);
    }

    public void updateRefs(String projPath, MetadataProject metadataProj, MetadataPackageHeader packageHeader) {
        RepositoryFactory.getInstance().getMetadataProjectRepository().updateRefs(getProjPath(projPath), metadataProj, packageHeader);
    }

    public void updateDbRefs(String projPath, MetadataProject metadataProj, MetadataPackageHeader packageHeader) {
        RepositoryFactory.getInstance().getMetadataProjectRepository().updateDbRefs(getProjPath(projPath), metadataProj, packageHeader);
    }

    public void updateIdpRefs(String projPath, MetadataProject metadataProj, ExternalInfo externalInfo) {
        RepositoryFactory.getInstance().getMetadataProjectRepository().updateIdpRefs(getProjPath(projPath), metadataProj, externalInfo);
    }

    public void updateProjectRefs(String projPath, MetadataProject metadataProj, ProjectHeader projectHeader) {
        RepositoryFactory.getInstance().getMetadataProjectRepository().updateProjectRefs(getProjPath(projPath), metadataProj, projectHeader);
    }

    public void updateMavenRefs(String projPath, MetadataProject metadataProj, MavenPackageRefs packageRefs) {
        RepositoryFactory.getInstance().getMetadataProjectRepository().updateMavenRefs(getProjPath(projPath), metadataProj, packageRefs);
    }

    public void updateMetadataProject(String projPath, MetadataProject metadataProject) {
        RepositoryFactory.getInstance().getMetadataProjectRepository().updateMetadataProject(getProjPath(projPath), metadataProject);
    }

    public Map<String, Map<String, Map<String, Long>>> getSourceDataModifiedTime(String absolutePath, List<String> types) {

        Map<String, Map<String, Map<String, Long>>> sourceDataModifiedTime = RepositoryFactory.getInstance().getMetadataProjectRepository().getSourceData(absolutePath);
        if (types.contains("metadata")) {
            Map<String, Long> metadataModifiedTime = getMetadataModifiedTime(absolutePath);
            sourceDataModifiedTime.get("modifiedtime").put("metadata", metadataModifiedTime);
        }
        if (types.contains("api_src")) {
            Map<String, Long> apiSrcModifiedTime = getCodeModifiedTime(absolutePath, "api_src");
            sourceDataModifiedTime.get("modifiedtime").put("api_src", apiSrcModifiedTime);
        }
        if (types.contains("api")) {
            Map<String, Long> apiModifiedTime = new HashMap<>();
            Map<String, Long> apiSrcModifiedTime = getCodeModifiedTime(absolutePath, "api_src");
            Map<String, Long> apiTargetModifiedTime = getCodeModifiedTime(absolutePath, "api_target");
            apiModifiedTime.putAll(apiSrcModifiedTime);
            apiModifiedTime.putAll(apiTargetModifiedTime);
            sourceDataModifiedTime.get("modifiedtime").put("api", apiModifiedTime);
        }
        if (types.contains("comp")) {
            Map<String, Long> compModifiedTime = getCodeModifiedTime(absolutePath, "comp");
            sourceDataModifiedTime.get("modifiedtime").put("comp", compModifiedTime);
        }
        if (types.contains("runtime")) {
            Map<String, Long> runtimeModifiedTime = getCodeModifiedTime(absolutePath, "runtime");
            sourceDataModifiedTime.get("modifiedtime").put("runtime", runtimeModifiedTime);
        }
        return sourceDataModifiedTime;
    }

    public Map<String, Long> getCodeModifiedTime(String absolutePath, String type) {
        Map<String, Long> codeModifiedTime = new HashMap<>();
        String relativePath = "";
        switch (type) {
            case "api_src":
                relativePath = Utils.getApiSourceDir();
                break;
            case "api_target":
                relativePath = Utils.getApiTargetDir();
                break;
            case "comp":
                relativePath = Utils.getCompModuleDir();
                break;
            case "runtime":
                relativePath = Utils.getRuntimeSourceDir();
                break;
            default:
                break;
        }
        String codePath = fileService.getCombinePath(absolutePath, relativePath);
        if (!fileService.isDirectoryExist(codePath)) {
            codeModifiedTime.put("nofile", (long) 0);
            return codeModifiedTime;
        }
        //获取目录下的所有文件路径
        List<String> filePaths = new ArrayList<>();
        getAllFilesUnderDir(filePaths, codePath);
        for (String filePath : filePaths) {
            codeModifiedTime.put(filePath.replace(absolutePath, "."), new File(filePath).lastModified());
        }
        return codeModifiedTime;
    }

    public boolean containsCode(String absolutePath, String type) {
        String relativePath = "";
        if ("comp".equals(type)) {
            relativePath = Utils.getCompModuleDir();
        }
        String codePath = fileService.getCombinePath(absolutePath, relativePath + File.separator + "src");
        List<String> filePaths = new ArrayList<>();
        getAllFilesUnderDir(filePaths, codePath);
        for (String filePath : filePaths) {
            if (filePath.endsWith(".java")) {
                return true;
            }
        }
        return false;
    }

    /***
     * 根据传过来的文件或者文件夹相对路径，获取当前文件或者文件夹所在的工程目录。查找当前目录或者上级目录。不检查下级目录。
     * @author zhongchq
     * @param absolutePath
     * 绝对路径
     * @return java.lang.String
     **/
    public String getProjPath(String absolutePath) {
        if (absolutePath == null || absolutePath.isEmpty()) {
            return null;
        }
        String projPath;
        // 如果是文件
        File file = new File(absolutePath);
        if (file.exists()) {
            try {
                file = file.getCanonicalFile();
                absolutePath = file.getCanonicalPath();
            } catch (IOException ignore) {
            }
        }
        absolutePath = file.isFile() ? file.getParent() : absolutePath;

        // 当前目录是否是工程目录
        projPath = isExistGspProjFileInCurPath(absolutePath) ? absolutePath : getProjPath(file.getParent());

        return projPath;
    }


    public boolean isExistGspProjFileInCurPath(String absolutePath) {
        List<File> files = fileService.getAllFiles(absolutePath);
        if (files.size() == 0) {
            return false;
        }
        for (File file : files) {
            if (fileService.getFileName(Utils.handlePath(file.toString())).equals(Utils.getGspProjectName())) {
                return true;
            }
        }
        return false;
    }

    public List<String> getProjectsInBoByProjPath(String projPath) {
        String boPath = new File(projPath).getParent();
        return getProjectsInBo(boPath);
    }

    public List<String> getProjectsInBo(String boPath) {
        List<String> projects = new ArrayList<>();
        final File[] files = new File(boPath).listFiles();
        if (files == null) {
            return projects;
        }
        for (File file : files) {
            if (isExistGspProjFileInCurPath(file.getPath())) {
                projects.add(file.getAbsolutePath());
            }
        }
        return projects;
    }

    public List<String> getProjectsInSuByProjPath(String projPath) {
        File suFile = new File(projPath).getParentFile().getParentFile();
        File[] boFiles = suFile.listFiles(File::isDirectory);
        List<String> projects = new ArrayList<>();
        if (Objects.nonNull(boFiles)) {
            for (File boFile : boFiles) {
                projects.addAll(getProjectsInBo(boFile.getPath()));
            }
        }
        return projects;
    }

    public String getMdprojPath(String projPath) {
        File file = getMdprojFile(projPath);
        if (file == null) {
            return null;
        }
        return file.getPath();
    }

    public File getMdprojFile(String projPath) {
        List<File> files = fileService.getAllFiles(projPath + File.separator + Utils.getMetadataProjPath());
        for (File file : files) {
            if (fileService.getExtension(file.getPath()).equals(Utils.getMetadataProjSuffix())) {
                return file;
            }
        }
        return null;
    }

    public String getMdpkgPath(String projPath) {
        List<File> files = fileService.getAllFiles(projPath + File.separator + Utils.getMetadataProjPath() + File.separator + Utils.getMetadataBinPath());
        for (File file : files) {
            if (fileService.getExtension(file.getPath()).equals(Utils.getMetadataPackageExtension())) {
                return file.getPath();
            }
        }
        return null;
    }

    public String getMavenUpdateFlag(String projPath) {
        return RepositoryFactory.getInstance().getMetadataProjectRepository().getMavenUpdateFlag(projPath);
    }

    public void setMavenUpdateFlag(String projPath, Boolean flag) {
        RepositoryFactory.getInstance().getMetadataProjectRepository().setMavenUpdateFlag(projPath, flag);
    }

    public String getMdpkgNameExistInBo(String projPath, String mdpkgName) {
        List<String> projectsInBo = getProjectsInBoByProjPath(projPath);
        projectsInBo.remove(projPath);
        for (String project : projectsInBo) {
            MetadataProject metadataProjInfo = getMetadataProjInfo(project);
            if (metadataProjInfo != null && mdpkgName.equals(metadataProjInfo.getMetadataPackageInfo().getName())) {
                return project;
            }
        }
        return null;
    }

    private MetadataProject getMetadataProjDirectly(String path) {
        MetadataProject project = new MetadataProject();
        List<File> files = fileService.getAllFiles(path);
        if (files.size() == 0) {
            return null;
        }
        for (File file : files) {
            if (fileService.getExtension(file.getPath()).equals(Utils.getMetadataProjSuffix())) {
                String projStr;
                try {
                    projStr = fileService.fileRead(Utils.handlePath(file.getPath()));
                    ObjectMapper objectMapper = Utils.getMapper();
                    project = objectMapper.readValue(projStr, MetadataProject.class);
                    String projPath = Utils.handlePath(file.getPath()).substring(0, Utils.handlePath(file.getPath()).lastIndexOf('/'));
                    String projFileName = Utils.handlePath(file.getPath()).substring(Utils.handlePath(file.getPath()).lastIndexOf('/') + 1);
                    project.setProjectPath(projPath);
                    project.setProjectFileName(projFileName);
                    project.setCsprojAssemblyName(project.getMetadataPackageInfo().getName());
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
        }

        return project;
    }

    private void getAllFilesUnderDir(List<String> filePathList, String dirPath) {
        List<File> dirs = fileService.getDirectorys(dirPath);
        if (dirs.size() > 0) {
            for (File dir : dirs) {
                String dirName = fileService.getFileName(Utils.handlePath(dir.getPath()));
                String temPath = dirPath + "/" + dirName;
                getAllFilesUnderDir(filePathList, temPath);
            }
        }
        var paths = fileService.getAllFiles(dirPath);
        if (paths.size() > 0) {
            for (File path : paths) {
                filePathList.add(Utils.handlePath(path.getPath()));
            }
        }
    }

    public List<String> getProjPathsInBoByRefedProjPath(String projPath) {
        List<String> projPaths = new ArrayList<>();
        File project = new File(getProjPath(projPath));
        File bo = project.getParentFile();
        File[] projectsInBo = bo.listFiles();
        if (projectsInBo != null) {
            for (File file : projectsInBo) {
                try {
                    final MetadataProject metadataProjInfo = getMetadataProjInfo(file.getPath());
                    if (metadataProjInfo.getProjectRefs() != null && metadataProjInfo.getProjectRefs().size() > 0) {
                        if (metadataProjInfo.getProjectRefs().stream().anyMatch(ref -> ref.getProjectPath().endsWith(project.getName()))) {
                            projPaths.add(file.getPath());
                        }
                    }
                } catch (Exception ignored) {
                }
            }
        }
        return projPaths;
    }

    public void getProjPathsInPath(String path, List<String> projPaths) {
        List<File> files = fileService.getAllFiles(path);
        List<File> dirs = fileService.getDirectorys(path);
        if (files != null && files.size() > 0) {
            for (File file : files) {
                String extension = fileService.getFileName(file.getPath());
                if (extension.toLowerCase().equals(Utils.getGspProjectName())) {
                    projPaths.add(file.getParent());
                    return;
                }
            }
        }
        if (dirs != null && dirs.size() > 0) {
            dirs.forEach(item -> getProjPathsInPath(item.getPath(), projPaths));
        }
    }

    public Map<String, MdprojInfoDto> transformToMdprojInfoDto(List<String> projPaths) {
        Map<String, MdprojInfoDto> mdprojInfos = new ConcurrentHashMap<>();
        projPaths.stream().parallel().forEach(projPath -> {
            MetadataProject metadataProjInfo = getMetadataProjInfo(projPath);
            if (metadataProjInfo != null) {
                MdprojInfoDto mdprojInfoDto = new MdprojInfoDto();
                mdprojInfoDto.setMetadataProject(metadataProjInfo);
                mdprojInfoDto.setProjPath(projPath);
                mdprojInfos.put(metadataProjInfo.getMetadataPackageInfo().getName(), mdprojInfoDto);
            }
        });
        return mdprojInfos;
    }

    public boolean isExistProjFileRecursively(String absolutePath) {
        // 如果是文件
        File file = new File(absolutePath);
        if (file.isFile()) {
            absolutePath = file.getParent();
        }
        boolean exist = isExistGspProjFileInCurPath(absolutePath);
        if (exist) {
            return true;
        }

        // 获取下级目录
        File[] files = new File(absolutePath).listFiles(File::isDirectory);
        if (files != null) {
            for (File subDir : files) {
                exist = isExistProjFileRecursively(subDir.getPath());
                if (exist) {
                    return true;
                }
            }
        }
        return false;
    }

    public String getBoPath(String absolutePath) {
        String projPath = getProjPath(absolutePath);
        return new File(projPath).getParent();
    }

    public boolean isInterpretation(String absolutePath) {
        ProcessMode processMode = getProcessMode(absolutePath);
        return ProcessMode.interpretation.equals(processMode);
    }

    public List<BOInfo> getBOInfo(String[] boids) {
        List<BOInfo> boInfoList = new ArrayList<>();
        BoService boService = BoUtils.getBoService();
        if (Objects.isNull(boService)) {
            return boInfoList;
        }
        for (String boId : boids) {
            String boPath = boService.getBoPath(boId);
            Map<String, String> projPaths = getPathOfBOProjects(ManagerUtils.getAbsolutePath(boPath));
            if (projPaths.size() > 0) {
                BOInfo boInfo = new BOInfo();
                boInfo.setId(boId);
                for (String projPath : projPaths.values()) {
                    try {
                        GspProject gspProjectInfo = new GspProjectCoreService().getGspProjectInfo(projPath);
                        switch (gspProjectInfo.getProjectType()) {
                            case backend:
                                boInfo.setBackendProjectCount(boInfo.getBackendProjectCount() + 1);
                                break;
                            case frontend:
                                boInfo.setFrontendProjectCount(boInfo.getFrontendProjectCount() + 1);
                                break;
                        }
                    } catch (Exception e) {
                        log.error("无法读取工程{}的信息。", projPath);
                    }
                }
                boInfo.setProjectCount(projPaths.size());
                boInfoList.add(boInfo);
            }
        }
        return boInfoList;
    }

    public String getJavaCompProjectPath(String absolutePath) {
        String projPath = getProjPath(absolutePath);
        return Paths.get(projPath).resolve(Utils.getCompModuleDir()).resolve(Utils.getSrcJavaPath()).toString();
    }

    public String getJavaProjectPath(String absolutePath) {
        String projPath = getProjPath(absolutePath);
        return Paths.get(projPath).resolve(Utils.getJavaProjectPath()).resolve(Utils.getCodePath()).toString();
    }

    private Map<String, String> getPathOfBOProjects(String boPath) {
        Map<String, String> paths = new HashMap<>();
        File boFile = new File(boPath);
        if (boFile.isDirectory() && boFile.exists()) {
            File[] files = boFile.listFiles(File::isDirectory);
            if (files != null) {
                for (File file : files) {
                    boolean isProjectFlag = new File(Paths.get(file.getPath()).resolve(Utils.getGspProjectName()).toString()).exists();
                    if (isProjectFlag) {
                        paths.put(file.getName(), file.getPath());
                    }
                }
            }
        }
        return paths;
    }

    public ConcurrentHashMap<String, MdprojInfoDto> transformToMdprojInfoDto(ArrayList<String> projPaths) {
        ConcurrentHashMap<String, MdprojInfoDto> mdprojInfos = new ConcurrentHashMap<>();
        projPaths.stream().parallel().forEach(projPath -> {
            MetadataProject metadataProjInfo = getMetadataProjInfo(projPath);
            if (metadataProjInfo != null) {
                MdprojInfoDto mdprojInfoDto = new MdprojInfoDto();
                mdprojInfoDto.setMetadataProject(metadataProjInfo);
                mdprojInfoDto.setProjPath(projPath);
                mdprojInfos.put(metadataProjInfo.getMetadataPackageInfo().getName(), mdprojInfoDto);
            }
        });
        return mdprojInfos;
    }

    public void changeProcessMode(String absolutePath, ProcessMode processMode) {
        // 更改字段
        String projPath = getProjPath(absolutePath);
        MetadataProject metadataProjInfo = getMetadataProjInfo(projPath);
        if (metadataProjInfo == null) {
            throw new RuntimeException("无法找到工程：" + absolutePath);
        }
        metadataProjInfo.getMetadataPackageInfo().setProcessMode(processMode);
        RepositoryFactory.getInstance().getMetadataProjectRepository().updateMetadataProject(projPath, metadataProjInfo);

        // 更改当前bo中是否存在引用他的前端工程
        PackageCoreService packageCoreService = new PackageCoreService();
        List<String> projectsInBo = getProjectsInBoByProjPath(absolutePath);
        projectsInBo.forEach(projectPath -> {
            MetadataProject metadataProjInfoInBo = getMetadataProjInfo(projectPath);
            if (metadataProjInfoInBo == null) {
                log.info("无法找到bo内工程：" + projectPath);
                return;
            }
            if (!CollectionUtils.isEmpty(metadataProjInfoInBo.getMetadataPackageRefs())) {
                metadataProjInfoInBo.getMetadataPackageRefs().forEach(ref -> {
                    if (ref.getName().equals(metadataProjInfo.getMetadataPackageInfo().getName())) {
                        packageCoreService.changeRefProcessMode(projectPath, ref.getName(), processMode);
                    }
                });
            }
        });

        // 代码修改，删除父pom中的api和core，修改comp.pom中的依赖，删除api和core
        // 删除父pom中的api和core
        List<String> modules = new ArrayList<>();
        modules.add(PomManager.API);
        modules.add(PomManager.CORE);
        PomManager pomManager = new PomManager();
        pomManager.deleteModules(pomManager.getParentPomPath(projPath), modules);
        // 删除comp中对api的依赖
        pomManager.deleteApiDependency(projPath, PomManager.COMP);
        // 把api.pom中的依赖，拷贝到comp.pom中
        pomManager.transferDependencies(projPath, PomManager.API, PomManager.COMP);
        // 删除api和core
        try {
            fileService.deleteAllFilesUnderDirectory(FileServiceImp.combinePath(projPath, Utils.getJavaProjectPath(), Utils.getCodePath(), PomManager.API));
            fileService.deleteAllFilesUnderDirectory(FileServiceImp.combinePath(projPath, Utils.getJavaProjectPath(), Utils.getCodePath(), PomManager.CORE));
        } catch (IOException e) {
            log.info("删除api和core代码失败：" + projPath, e);
        }

        // 删除元数据包
        PackageGenerateCoreService packageGenerateCoreService = new PackageGenerateCoreService();
        packageGenerateCoreService.deleteMdpkg(projPath);
    }

    public String getProjectBinPath(String absolutePath) {
        String projPath = getProjPath(absolutePath);
        return FileServiceImp.combinePath(projPath, Utils.getMetadataProjPath(), Utils.getMetadataBinPath());
    }

    public String getMdpkgName(String absolutePath) {
        MetadataProject metadataProjInfo = getMetadataProjInfo(absolutePath);
        if (metadataProjInfo == null) {
            throw new RuntimeException("获取工程信息失败：" + absolutePath);
        }
        return metadataProjInfo.getMetadataPackageInfo().getName() + Utils.getMetadataPackageExtension();
    }

    public boolean isMetadataChanged(String absolutePath, OperationEnum operationEnum, FileFilter fileFilter) {
        String projPath = getProjPath(absolutePath);
        List<String> metadataPaths = getMetadataPathsToDetect(projPath, operationEnum, fileFilter);
        return RepositoryFactory.getInstance().getSourceDataRepository().isMetadataChanged(projPath, operationEnum, metadataPaths);
    }

    public void updateMetadataChanges(String absolutePath, OperationEnum operationEnum, FileFilter fileFilter) {
        String projPath = getProjPath(absolutePath);
        List<String> metadataPaths = getMetadataPathsToDetect(projPath, operationEnum, fileFilter);
        RepositoryFactory.getInstance().getSourceDataRepository().updateMetadataChanges(projPath, operationEnum, metadataPaths);
    }

    public boolean isRefMetadataChanged(String absolutePath, OperationEnum operationEnum, MetadataURI metadataURI) {
        String projPath = getProjPath(absolutePath);
        GspMetadata refMetadata = SpringBeanUtils.getBean(RefCommonService.class).getRefMetadata(metadataURI, null, projPath);
        String contentStr = new MetadataSerializer().serialize(refMetadata);
        return RepositoryFactory.getInstance().getSourceDataRepository().isContentChanged(projPath, metadataURI.getId(), contentStr, operationEnum);
    }

    public void updateRefMetadataChanged(String absolutePath, OperationEnum operationEnum, MetadataURI metadataURI) {
        String projPath = getProjPath(absolutePath);
        GspMetadata refMetadata = SpringBeanUtils.getBean(RefCommonService.class).getRefMetadata(metadataURI, null, projPath);
        String contentStr = new MetadataSerializer().serialize(refMetadata);
        RepositoryFactory.getInstance().getSourceDataRepository().updateContentChanges(projPath, metadataURI.getId(), contentStr, operationEnum);
    }

    public boolean isFileChanged(String absolutePath, OperationEnum operationEnum, FilenameFilter filenameFilter) {
        String projPath = getProjPath(absolutePath);
        return RepositoryFactory.getInstance().getSourceDataRepository().isFileChanged(projPath, absolutePath, operationEnum, filenameFilter);
    }

    public boolean isFileChanged(String absolutePath, String key, String value, OperationEnum operationEnum) {
        String projPath = getProjPath(absolutePath);
        return RepositoryFactory.getInstance().getSourceDataRepository().isFileChanged(projPath, key, value, operationEnum);
    }

    public void updateFileChanges(String absolutePath, OperationEnum operationEnum, FilenameFilter filenameFilter) {
        String projPath = getProjPath(absolutePath);
        RepositoryFactory.getInstance().getSourceDataRepository().updateFileChanges(projPath, absolutePath, operationEnum, filenameFilter);
    }

    public void updateFileChanges(String absolutePath, String key, String value, OperationEnum operationEnum) {
        String projPath = getProjPath(absolutePath);
        RepositoryFactory.getInstance().getSourceDataRepository().updateFileChanges(projPath, key, value, operationEnum);
    }

    public boolean isContentChanged(String absolutePath, String contentStr, OperationEnum operationEnum) {
        String projPath = getProjPath(absolutePath);
        String key = FileServiceImp.getRelativePath(absolutePath, projPath);
        return RepositoryFactory.getInstance().getSourceDataRepository().isContentChanged(projPath, key, contentStr, operationEnum);
    }

    public void updateContentChanges(String absolutePath, String contentStr, OperationEnum operationEnum) {
        String projPath = getProjPath(absolutePath);
        String key = FileServiceImp.getRelativePath(absolutePath, projPath);
        RepositoryFactory.getInstance().getSourceDataRepository().updateContentChanges(projPath, key, contentStr, operationEnum);
    }

    public List<File> getChangeFiles(String absolutePath, OperationEnum operationEnum) {
        String projPath = getProjPath(absolutePath);
        return RepositoryFactory.getInstance().getSourceDataRepository().getChangeFiles(projPath, absolutePath, operationEnum);
    }

    public void deleteChangeRecord(String absolutePath, OperationEnum operationEnum) {
        String projPath = getProjPath(absolutePath);
        RepositoryFactory.getInstance().getSourceDataRepository().deleteChangeRecord(projPath, operationEnum);
    }

    public String getSrcPathByModule(String absolutePath, Module module) {
        String projPath = getProjPath(absolutePath);
        return FileServiceImp.combinePath(projPath, Utils.getJavaProjectPath(), Utils.getCodePath(), module.toString(), Utils.getSrcPath());
    }

    public String getTargetPathByModule(String absolutePath, Module module) {
        String projPath = getProjPath(absolutePath);
        return FileServiceImp.combinePath(projPath, Utils.getJavaProjectPath(), Utils.getCodePath(), module.toString(), Utils.getTargetPath());
    }

    private List<String> getMetadataPathsToDetect(String projPath, OperationEnum operationEnum, FileFilter fileFilter) {
        List<String> metadataPaths = MetadataCoreManager.getCurrent().getMetadataPaths(projPath, Utils.getMetadataPostfixTypes(operationEnum));
        if (fileFilter != null) {
            metadataPaths = metadataPaths.stream().filter(metadataPath -> fileFilter.accept(new File(metadataPath))).collect(Collectors.toList());
        }
        return metadataPaths;
    }
}
